<template>
  <div style="width: 100%; height: 100%; overflow: hidden;">
    <div class="canvas-toolbar">
      <div class="float-left">
        <Poptip transfer trigger="click" placement="bottom"
                @on-popper-show="styleFilterSettingModalVisible = true"
                @on-popper-hide="styleFilterSettingModalVisible = false">
          <Tooltip transfer :content="'滤镜配置'" placement="bottom">
            <Button size="small" class="m-r-5px" type="primary"><Icon custom="iconfont icon-style-filter"/></Button>
          </Tooltip>
          <div slot="content">
            <StyleFilterSettingModal ></StyleFilterSettingModal>
          </div>
        </Poptip>

        <LayoutItemSearch class="m-l-5px"></LayoutItemSearch>
      </div>
    </div>

    <div id="ReactiveLayoutContainer" class="reactive-layout-container" @mousedown="layoutCanvasClick" @contextmenu.stop="onLayoutCanvasRightClickHandler">
      <div id="ReactiveLayout"
           class="reactive-layout-canvas"
           :style="Object.assign({
             width: layout.layoutConfigData.canvasWidth == 0 ? '100%': layout.layoutConfigData.canvasWidth + 'px',
             backgroundColor: layout.layoutConfigData.backgroundColor,
             outline: canvasEnabled ? '1px solid #4AFFFF': 'none',
             backgroundImage: layout.layoutConfigData.imageRelativePath ? 'url('+canvasBackgroundImageSrc+')': '',
             backgroundRepeat: layout.layoutConfigData.imageRelativePath ? layout.layoutConfigData.imageRepeat: '',
             backgroundSize: layout.layoutConfigData.imageRelativePath ? layout.layoutConfigData.imageSize: '',
           }, $PnUtil.cssToVueStyleObj(layout.layoutConfigData.customStyleCode))">

        <grid-layout
            :layout.sync="gridLayoutItems"
            :col-num="layout.layoutConfigData.colNum"
            :row-height="layout.layoutConfigData.rowHeight"
            :max-rows="layout.layoutConfigData.maxRows"
            :margin="[layout.layoutConfigData.marginHorizontal, layout.layoutConfigData.marginVertical]"
            :is-draggable="layout.layoutConfigData.isDraggable"
            :is-resizable="layout.layoutConfigData.isResizable"
            :is-mirrored="layout.layoutConfigData.isMirrored"
            :auto-size="layout.layoutConfigData.autoSize"
            :vertical-compact="layout.layoutConfigData.verticalCompact"
            :prevent-collision="layout.layoutConfigData.preventCollision"
            :use-css-transforms="layout.layoutConfigData.useCssTransforms"
            :responsive="false"
        >
          <grid-item class="reactive-grid-item"
                     v-for="layoutItem in gridLayoutItems"
                     :x="layoutItem.x"
                     :y="layoutItem.y"
                     :w="layoutItem.w"
                     :h="layoutItem.h"
                     :i="layoutItem.i"
                     :key="layoutItem.i"
                     :isDraggable="layoutItem.layoutItemConfigData.draggableEnabled"
                     :isResizable="layoutItem.layoutItemConfigData.draggableEnabled"
                     :static="layoutItem.layoutItemConfigData.isStatic"
                     :current-select-grid-item-id="currentSelectLayoutItemId"
                     @activated="onGridItemActivated(layoutItem)"
                     @moved="onGridItemMoved"
                     @resized="onGridItemResized">
            <div class="reactive-layout-item"
                 :class="buildLayoutItemActiveClass(layoutItem) + buildLayoutItemCustomClassName(layoutItem)"
                 :style="{
                   perspective: layoutItem.layoutItemConfigData.use3dRotate ? layoutItem.layoutItemConfigData.perspectiveFor3d + 'px' : 'none',
                 }"
                 :id="layoutItem.i"
                 :data-id="layoutItem.i"
                 @click.stop="layoutItemClick(layoutItem, $event)"
                 @contextmenu.stop="onLayoutItemRightClickHandler($event, layoutItem.layoutItemConfigData, layoutItem.component)">
              <div class="w-h-full"
                   :style="{
                     transform: buildLayoutItem3dTransformStyleValue(layoutItem),
                     opacity: layoutItem.layoutItemConfigData.opacity
                   }">
                <component :is="layoutItem.name" :layoutItemId="layoutItem.id" :toggleIsLoaded="layoutItem.component.id"
                           :style="{
                             backgroundImage: layoutItem.layoutItemConfigData.imageRelativePath ? 'url('+layoutItemBackgroundImageSrc(layoutItem)+')': '',
                             backgroundRepeat: layoutItem.layoutItemConfigData.imageRelativePath ? layoutItem.layoutItemConfigData.imageRepeat: '',
                             backgroundSize: layoutItem.layoutItemConfigData.imageRelativePath ? layoutItem.layoutItemConfigData.imageSize: '',
                           }">
                  <FuncCompContainer :layout-item="layoutItem" :style="{filter: buildLayoutItemStyleFilter(layout.layoutConfigData)}">
                    <component v-if="layoutItem.component.id" :ref="layoutItem.component.id" :is="layoutItem.component.name" :location="layoutItem.id"></component>
                  </FuncCompContainer>
                </component>
              </div>

            </div>
          </grid-item>
          <!-- 构建被隐藏的组件 -->
          <GridItemHidden v-for="(layoutItem, index) in hiddenGridLayoutItems" :key="index">
            <div class="reactive-layout-item">
              <div class="w-h-full">
                <component :is="layoutItem.name" :layoutItemId="layoutItem.id">
                  <FuncCompContainer :layout-item="layoutItem">
                    <component v-if="layoutItem.component.id" :ref="layoutItem.component.id" :is="layoutItem.component.name" :location="layoutItem.id"></component>
                  </FuncCompContainer>
                </component>
              </div>
            </div>
          </GridItemHidden>
        </grid-layout>
      </div>
    </div>

    <v-contextmenu ref="layoutCanvasContextmenu">
      <v-contextmenu-item @click="layoutCanvasContextmenuClick('layoutCanvasMenu_clearCanvas')"><Icon custom="iconfont iconqingkong"/> 清空画布</v-contextmenu-item>
    </v-contextmenu>

    <v-contextmenu ref="contextmenu">
      <v-contextmenu-item v-show="contentMenuOption.unbindComponent" @click="layoutItemContextMenuClick('menu_unbindComponent')"><Icon custom="iconfont icon-unbind"/> 解绑关联组件</v-contextmenu-item>
      <v-contextmenu-item v-show="contentMenuOption.unbindComponent" divider></v-contextmenu-item>
      <v-contextmenu-item @click="layoutItemContextMenuClick('menu_hide')"><Icon type="md-eye-off" size="14"/> 隐藏</v-contextmenu-item>
      <v-contextmenu-item v-show="currentSelectLayoutItemIds.length > 1 ? true : !contentMenuOption.layoutItemLockStatus"
                          @click="layoutItemContextMenuClick('menu_lock')"><Icon custom="iconfont iconlock" /> 锁定</v-contextmenu-item>
      <v-contextmenu-item v-show="currentSelectLayoutItemIds.length > 1 ? true : contentMenuOption.layoutItemLockStatus"  @click="layoutItemContextMenuClick('menu_unlock')"><Icon custom="iconfont iconunlock" /> 解锁</v-contextmenu-item>
      <v-contextmenu-item divider></v-contextmenu-item>
      <v-contextmenu-item @click="layoutItemContextMenuClick('menu_delLayoutItem')"><Icon type="md-trash" size="15"/> 删除布局块</v-contextmenu-item>
    </v-contextmenu>

  </div>
</template>

<script>

import GridLayout from '../common/vue-grid-layout/components/GridLayout'
import GridItem from '../common/vue-grid-layout/components/GridItem'
import GridItemHidden from '../common/vue-grid-layout/components/GridItemHidden'

import FuncCompContainer from '../common/FuncCompContainer'
import StyleFilterSettingModal from '../common/StyleFilterSettingModal'
import LayoutItemSearch from '../common/LayoutItemSearch'

import CanvasDesignerMixin from '@/mixin/CanvasDesignerMixin'
import ReactiveLayoutGlobalMixin from '@/mixin/ReactiveLayoutGlobalMixin'

import {COL_NUM, LAYOUT_ITEM_W} from '@/utils/ReactiveDesignerCommonUtil'

import { createHelpers } from 'vuex-map-fields';
const { mapFields } = createHelpers({
  getterType: 'designer/getField',
  mutationType: 'designer/updateField',
});

export default {
  name: "ReactiveLayoutCanvas",
  components: {
    GridLayout,
    GridItem,
    GridItemHidden,
    FuncCompContainer,
    StyleFilterSettingModal,
    LayoutItemSearch
  },
  mixins: [CanvasDesignerMixin, ReactiveLayoutGlobalMixin],
  data() {
    return {
      $ReactiveLayout: null, // 存储画布JQuery对象
      tmpCurrentSelectLayoutItemId: '',

      styleFilterSettingModalVisible: false,

      contentMenuOption: {
        unbindComponent: true,
        layoutItemLockStatus: false,
      }
    }
  },
  destroyed() {
    this.$EventBus.$off('pn_showLayoutCanvasContextmenu')
  },
  mounted() {
    this.$ReactiveLayout = $('#ReactiveLayout');

    // 设计器初始化之后先给画布的放置事件配置scope为compCardScope
    setTimeout(()=>{
      $('#ReactiveLayout').droppable('option', 'scope', 'compCardScope');
    }, 100);

    this.buildGridLayoutItems();
    this.registerCanvasDrop();
    this.registerLayoutItemDrop();
    this.registerKeyDownAndUp();

    this.$nextTick(()=>{
      this.$EventBus.$on('pn_showLayoutCanvasContextmenu', (event) => {
        this.$refs.layoutCanvasContextmenu.show(this.$DesignerCommonUtil.calculateContextmenuPosition(
            event.pageX, event.pageY, this.$(this.$refs.layoutCanvasContextmenu.$el).outerHeight()
        ))
      })
    })

  },
  methods: {

    onGridItemActivated (layoutItem) {
      this.registerKeyDownAndUp(); // 重新注册键盘监听

      this.tmpCurrentSelectLayoutItemId = layoutItem.id;

      // 点击布局块的时候，清空画布的scope，然后给布局块设置droppable的属性scope为compCardScope，
      // 与组件库拖拽对象的scope对应，这样组件库的拖拽对象就可以放置在当前点击的布局块里
      this.$ReactiveLayout.droppable('option', 'scope', '');
      $('.reactive-layout-item').droppable('option', 'scope', '');
      $('#'+layoutItem.id).droppable('option', 'scope', 'compCardScope');

      // 如果当前点击的布局块不等于store中存储的currentSelectLayoutItemId，就清空功能组件配置表单
      // (这一段代码是为了解决点击同一个布局块，会刷新功能组件表单的问题)
      if (this.$store.state.designer.currentSelectLayoutItemId != this.tmpCurrentSelectLayoutItemId) {
        this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', '');
      }

      this.$store.commit('designer/setCurrentSelectLayoutItemId', this.tmpCurrentSelectLayoutItemId);
    },

    onGridItemMoved (i, newX, newY) {
      // 这里必须要更新当前被拖动布局块之外的其他布局块的x和y值，否则会出现拖动后位置没有置于该在的位置
      this.gridLayoutItems.forEach(gridLayoutItem => {
        if (gridLayoutItem.id != i) {
          this.$store.commit('designer/setReactiveLayoutItemXAndY', {id: gridLayoutItem.id, x: gridLayoutItem.x, y: gridLayoutItem.y})
        }
      })
      this.$store.commit('designer/setReactiveLayoutItemXAndY', {id: i, x: newX, y: newY})
    },

    onGridItemResized (i, newH, newW, newHPx, newWPx) {
      this.gridLayoutItems.forEach(gridLayoutItem => {
        if (gridLayoutItem.id != i) {
          this.$store.commit('designer/setReactiveLayoutItemWAndH', {id: gridLayoutItem.id, w: gridLayoutItem.w, h: gridLayoutItem.h})
        }
      })
      this.$store.commit('designer/setReactiveLayoutItemWAndH', {id: i, w: newW, h: newH})
    },

    /**
     * 注册画布放置监听
     */
    registerCanvasDrop () {
      let _this = this;

      this.$ReactiveLayout.droppable({
        accept: ".comp-card",
        drop: (e, ui) => {
          let compName = ui.draggable[0].attributes['data-component'].nodeValue;
          let baseAttr = _this.$PnUtil.getCompBaseAttr(compName);

          // 判断是否放置的是布局块组件
          if (compName == 'DefaultLayoutItem') {
            let newLayoutItem = this.$ReactiveDesignerCommonUtil.buildReactiveLayoutItem(
                (this.gridLayoutItems.length * LAYOUT_ITEM_W) % COL_NUM,
                this.gridLayoutItems.length + COL_NUM
            )
            _this.$store.commit('designer/addLayoutItem', newLayoutItem);
          }else {

            // 如果data-id不等于空，则为收藏的组件
            if (ui.draggable[0].attributes['data-id']) {
              // let id = ui.draggable[0].attributes['data-id'].nodeValue;
              // _this.$PnApi.EnshrineCompApi.getLayoutItemObjById(id).then(result => {
              //   let newLayoutItem = result.data.payload;
              //   let type = ui.draggable[0].attributes['data-type'].nodeValue;
              //   if (type && type == 'F') {
              //
              //   }
              //   else if (type && type == 'G') {
              //     _this.$Message.warning('响应式栅格布局中，不能拖入组')
              //   }
              // })
              _this.$Message.warning('响应式栅格布局中，暂不支持添加收藏组件')
            }else {
              let newLayoutItem = _this.$ReactiveDesignerCommonUtil.buildReactiveLayoutItem(
                  (this.gridLayoutItems.length * LAYOUT_ITEM_W) % COL_NUM,
                  this.gridLayoutItems.length + COL_NUM
              );
              let component = {
                id: _this.$PnUtil.uuid(),
                name: compName,
                version: baseAttr.version,
                compConfigData: Object.assign({}, _this.$PnUtil.deepClone(_this.$PnUtil.getCompConfigData(compName)))
              };
              newLayoutItem.component = component;
              _this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', '');
              _this.$store.commit('designer/addLayoutItem', newLayoutItem);
              _this.$store.commit('designer/setCurrentSelectLayoutItemId', newLayoutItem.id);
              if (newLayoutItem.component.id) {
                _this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', newLayoutItem.component.name + 'Form');
              }
            }

          }

        }
      });
    },

    /**
     * 注册布局块放置事件
     */
    registerLayoutItemDrop () {
      let _this = this;

      setTimeout(() => {
        // 注册布局块放置监听
        $(".reactive-layout-item").droppable({
          accept: ".comp-card",
          // activeClass: "layout-item-drop-active",
          // hoverClass: "layout-item-drop-hover",
          drop: (e, ui) => {

            let layoutItemId = e.target.attributes['data-id'].nodeValue;
            let compName = ui.draggable[0].attributes['data-component'].nodeValue;
            let baseAttr = _this.$PnUtil.getCompBaseAttr(compName);

            if (compName == 'DefaultLayoutItem') {
              _this.$Message.warning('布局块不可多层嵌套！')
            }else {
              let addComponent = function (_layoutItemId) {
                let component = {
                  id: _this.$PnUtil.uuid(),
                  name: compName,
                  version: baseAttr.version,
                  compConfigData: Object.assign({}, _this.$PnUtil.deepClone(_this.$PnUtil.getCompConfigData(compName)))
                };

                _this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', '');
                _this.$store.commit('designer/addComponentToLayoutItem', {
                  layoutItemId: _layoutItemId,
                  component: component
                })
                _this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', component.name + 'Form');
              };

              // 如果data-id不等于空，则为收藏的组件
              if (ui.draggable[0].attributes['data-id']) {
                _this.$Message.warning({
                  content: '收藏的组件不可以替换布局块原本绑定的组件',
                  duration: 3
                })
              }else {
                let storeLayoutItem = this.$store.getters['designer/getLayoutItemById'](layoutItemId);
                if (storeLayoutItem.component.id) {
                  this.$Modal.confirm({
                    title: '提醒',
                    content: '目标布局块已关联组件，是否要覆盖？',
                    okText: '覆盖',
                    onOk: () => {
                      addComponent(layoutItemId)
                    }
                  })
                }else {
                  addComponent(layoutItemId)
                }
              }
            }

          }
        });
      }, 100)
    },

    onLayoutItemRightClickHandler (e, layoutItemConfigData, component) {
      e.preventDefault()
      if (this.currentSelectLayoutItemId || this.currentSelectLayoutItemIds.length > 0) {
        this.contentMenuOption.layoutItemLockStatus = !layoutItemConfigData.draggableEnabled

        if (!component.id) {
          this.contentMenuOption.unbindComponent = false
        }else {
          this.contentMenuOption.unbindComponent = true
        }
        this.$refs.contextmenu.show(this.$DesignerCommonUtil.calculateContextmenuPosition(e.pageX, e.pageY, this.$(this.$refs.contextmenu.$el).outerHeight()))
      }
    },

    layoutItemContextMenuClick (name) {
      let layoutItemIds = _.compact(_.union([this.currentSelectLayoutItemId], this.currentSelectLayoutItemIds.concat()));

      switch (name) {
        case 'menu_unbindComponent':
          this.$Modal.confirm({
            title: '提醒',
            content: '确认解除所选中布局块的关联组件吗？',
            onOk: () => {
              layoutItemIds.forEach(id => {
                this.$store.commit('designer/deleteComponentByLayoutItemId', id);
                this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', '')
              });
            }
          });
          break;
        case 'menu_hide':
          this.$DesignerCommonUtil.layoutItemHide(layoutItemIds);
          break;
        case 'menu_lock':
          this.$DesignerCommonUtil.layoutItemLock(layoutItemIds);
          break;
        case 'menu_unlock':
          this.$DesignerCommonUtil.layoutItemUnLock(layoutItemIds);
          break;
        case 'menu_delLayoutItem':
          this.$DesignerCommonUtil.layoutItemDelete(layoutItemIds);
          break;
      }
    },

    /**
     * 画布右键事件处理
     **/
    onLayoutCanvasRightClickHandler (e) {
      e.preventDefault();
      this.$EventBus.$emit('pn_showLayoutCanvasContextmenu', e)
    },

    layoutCanvasContextmenuClick (name) {
      switch (name) {
        case 'layoutCanvasMenu_clearCanvas':
          this.$DesignerCommonUtil.clearCanvas();
          break;
      }
    },

    /**
     * 画布点击事件监听
     */
    layoutCanvasClick () {
      this.$refs.layoutCanvasContextmenu.hide();

      // 设置画布激活状态为true
      this.$store.commit('designer/setCanvasEnabled', true);

      // 清空所有布局块的scope，然后给画布的放置配置设置scope为compCardScope
      $('.reactive-layout-item').droppable('option', 'scope', '');
      this.$ReactiveLayout.droppable('option', 'scope', 'compCardScope');

      this.$store.commit('designer/setRightSidebarFuncCompConfigFormName', '');
      this.$store.commit('designer/setCurrentSelectLayoutItemId', '');
      this.$store.commit('designer/setCurrentSelectLayoutItemIds', []);
    },

    /**
     * 布局块点击事件
     * @param layoutItem
     * @param event
     */
    layoutItemClick(layoutItem, event) {
      // 设置画布激活状态为false
      this.$store.commit('designer/setCanvasEnabled', false);

      this.$refs.contextmenu.hide();
      this.$refs.layoutCanvasContextmenu.hide();

      if(this.$store.getters['designer/getKeepCtrl']) {
        let selectLayoutItemIds = this.currentSelectLayoutItemIds.concat();
        if(selectLayoutItemIds.indexOf(layoutItem.id) > -1) {
          for (let i=0; i<selectLayoutItemIds.length; i++) {
            if(layoutItem.id == selectLayoutItemIds[i]) {
              if(selectLayoutItemIds.length > 1) {
                selectLayoutItemIds.splice(i, 1);
                i--
              }
            }
          }
        }else {
          selectLayoutItemIds.pushNoRepeat(layoutItem.id);
        }
        this.$store.commit('designer/setCurrentSelectLayoutItemIds', selectLayoutItemIds);
      }else {
        if (this.currentSelectLayoutItemIds.indexOf(layoutItem.id) == -1) {
          this.$store.commit('designer/setCurrentSelectLayoutItemIds', []) // 清除ctrl选中的布局块
        }
      }
    },


  },
  computed: {
    ...mapFields({
      layout: 'pageMetadata.layout',
      canvasEnabled: 'canvasEnabled',
    }),
    // 通过此计算函数来确认画布中的布局块位置尺寸、绑定组件是否改变，
    // 如果发生了改变，将触发watch去执行updateGridLayoutItems来重构gridLayoutItems数据
    watchingLayoutItems () {
      let collect = '';
      this.layout.layoutItems.forEach(layoutItem => {
        collect +=
            layoutItem.layoutItemConfigData.w +
            layoutItem.layoutItemConfigData.h +
            layoutItem.layoutItemConfigData.x +
            layoutItem.layoutItemConfigData.y +
            layoutItem.layoutItemConfigData.draggableEnabled +
            layoutItem.layoutItemConfigData.isStatic +
            layoutItem.layoutItemConfigData.opacity +
            layoutItem.layoutItemConfigData.display +
            layoutItem.layoutItemConfigData.use3dRotate +
            layoutItem.layoutItemConfigData.axisOfRotation +
            layoutItem.layoutItemConfigData.rotationAngleFor3d +
            layoutItem.layoutItemConfigData.perspectiveFor3d +
            layoutItem.layoutItemConfigData.imageRelativePath +
            layoutItem.layoutItemConfigData.imageRepeat +
            layoutItem.layoutItemConfigData.imageSize +
            layoutItem.id +
            layoutItem.component.id;
      })
      return collect
    }
  },
  watch: {
    'watchingLayoutItems': [
      {
        handler: function () {
          console.log('watchingLayoutItems')
          this.buildGridLayoutItems()
        }
      },
      {
        handler: 'registerLayoutItemDrop'
      }
    ],
    'currentSelectLayoutItemId': {
      handler: function (val) {
        if (val) {
          // 点击布局块的时候，清空画布的scope，然后给布局块设置droppable的属性scope为compCardScope，
          // 与组件库拖拽对象的scope对应，这样组件库的拖拽对象就可以放置在当前点击的布局块里
          this.$ReactiveLayout.droppable('option', 'scope', '');
          $('.reactive-layout-item').droppable('option', 'scope', '');
          $('#'+val).droppable('option', 'scope', 'compCardScope');
          if (this.canvasEnabled) {
            // 设置画布激活状态为false
            this.$store.commit('designer/setCanvasEnabled', false);
          }
        }
      }
    },
    // 监听画布是否被激活状态
    'canvasEnabled': {
      handler: function (val) {
        if (val) {
          // 清空所有布局块的scope，然后给画布的放置配置设置scope为compCardScope
          $('.reactive-layout-item').droppable('option', 'scope', '');
          this.$ReactiveLayout.droppable('option', 'scope', 'compCardScope');
        }
      }
    },
  }
}
</script>

<style lang="less" scoped>
.canvas-toolbar {
  width: 100%;
  height: 33px;
  border-bottom: 1px solid #424242;
  background-color: #616161;
  position: fixed;
  top: 48px;
  z-index: 3;
  line-height: 29px;
  padding-left: 20px;
}

.reactive-layout-container {
  width: 100%;
  height: calc(100% - 33px);
  overflow: auto;
  margin-top: 33px;
  padding: 20px;
  .reactive-layout-canvas {
    min-height: calc(100% - 0px);
  }
}

.reactive-layout-item {
  width: 100%;
  height: 100%;
  &.active {
    box-shadow: 0 0 10px #FFF;
    outline: 1px solid #4AFFFF;
  }
  &.ui-selecting {
    outline: 1px solid #4AFFFF;
  }
}

.vue-grid-item {
  border: 1px dashed #000;
}
</style>
